 /*******************************************************************************
  * Copyright (c) 2000, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal.presentations.r21.widgets;

 import org.eclipse.swt.SWT;
 import org.eclipse.swt.SWTError;
 import org.eclipse.swt.SWTException;
 import org.eclipse.swt.accessibility.ACC;
 import org.eclipse.swt.accessibility.Accessible;
 import org.eclipse.swt.accessibility.AccessibleAdapter;
 import org.eclipse.swt.accessibility.AccessibleControlAdapter;
 import org.eclipse.swt.accessibility.AccessibleControlEvent;
 import org.eclipse.swt.accessibility.AccessibleEvent;
 import org.eclipse.swt.events.SelectionListener;
 import org.eclipse.swt.graphics.Color;
 import org.eclipse.swt.graphics.Font;
 import org.eclipse.swt.graphics.GC;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.graphics.ImageData;
 import org.eclipse.swt.graphics.PaletteData;
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.graphics.RGB;
 import org.eclipse.swt.graphics.Rectangle;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.Listener;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.swt.widgets.ToolBar;
 import org.eclipse.swt.widgets.ToolItem;
 import org.eclipse.swt.widgets.TypedListener;

 /**
  * Instances of this class implement the notebook user interface
  * metaphor. It allows the user to select a notebook page from
  * set of pages.
  * <p>
  * The item children that may be added to instances of this class
  * must be of type <code>CTabItem</code>.
  * <code>Control</code> children are created and then set into a
  * tab item using <code>CTabItem#setControl</code>.
  * </p><p>
  * Note that although this class is a subclass of <code>Composite</code>,
  * it does not make sense to set a layout on it.
  * </p><p>
  * <dl>
  * <dt><b>Styles:</b></dt>
  * <dd>TOP, BOTTOM, FLAT</dd>
  * <dt><b>Events:</b></dt>
  * <dd>Selection</dd>
  * <dd>"CTabFolder"</dd>
  * </dl>
  * <p>
  * Note: Only one of the styles TOP and BOTTOM
  * may be specified.
  * </p><p>
  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
  * </p>
  */

 public class CTabFolder extends Composite {

     /**
      * marginWidth specifies the number of pixels of horizontal margin
      * that will be placed along the left and right edges of the form.
      *
      * The default value is 0.
      */
     public int marginWidth = 0;

     /**
      * marginHeight specifies the number of pixels of vertical margin
      * that will be placed along the top and bottom edges of the form.
      *
      * The default value is 0.
      */
     public int marginHeight = 0;

     /**
      * Color of innermost line of drop shadow border.
      */
     public static RGB borderInsideRGB = new RGB(132, 130, 132);

     /**
      * Color of middle line of drop shadow border.
      */
     public static RGB borderMiddleRGB = new RGB(143, 141, 138);

     /**
      * Color of outermost line of drop shadow border.
      */
     public static RGB borderOutsideRGB = new RGB(171, 168, 165);

     /*
      * A multiple of the tab height that specifies the minimum width to which a tab
      * will be compressed before scrolling arrows are used to navigate the tabs.
      */
     public int MIN_TAB_WIDTH = 3;

     /* sizing, positioning */
     int xClient, yClient;

     boolean onBottom = false;

     boolean fixedTabHeight;

     int tabHeight;

     /* item management */
     private CTabItem items[] = new CTabItem[0];

     private int selectedIndex = -1;

     int topTabIndex = -1; // index of the left most visible tab.

     /* External Listener management */
     private CTabFolderListener[] tabListeners = new CTabFolderListener[0];

     /* Color appearance */
     Image backgroundImage;

     Color[] gradientColors;

     int[] gradientPercents;

     Color selectionForeground;

     Color background;

     // internal constants
 private static final int DEFAULT_WIDTH = 64;

     private static final int DEFAULT_HEIGHT = 64;

     // scrolling arrows
 private ToolBar arrowBar;

     private Image arrowLeftImage;

     private Image arrowRightImage;

     private Control topRight;

     // close button
 boolean showClose = false;

     private Image closeImage;

     ToolBar closeBar;

     private ToolBar inactiveCloseBar;

     private CTabItem inactiveItem;

     // borders
 boolean showBorders = false;

     private int borderBottom = 0;

     private int borderLeft = 0;

     private int borderRight = 0;

     private int borderTop = 0;

     private Color borderColor1;

     private Color borderColor2;

     private Color borderColor3;

     // when disposing CTabFolder, don't try to layout the items or
 // change the selection as each child is destroyed.
 private boolean inDispose = false;

     // keep track of size changes in order to redraw only affected area
 // on Resize
 private Point oldSize;

     private Font oldFont;

     // insertion marker
 int insertionIndex = -2; // Index of insert marker. Marker always shown after index.

     // -2 means no insert marker

     // tool tip
 private Shell tip;

     private Label label;

     private boolean showToolTip = false;

     private CTabItem toolTipItem;

     /**
      * Constructs a new instance of this class given its parent
      * and a style value describing its behavior and appearance.
      * <p>
      * The style value is either one of the style constants defined in
      * class <code>SWT</code> which is applicable to instances of this
      * class, or must be built by <em>bitwise OR</em>'ing together
      * (that is, using the <code>int</code> "|" operator) two or more
      * of those <code>SWT</code> style constants. The class description
      * lists the style constants that are applicable to the class.
      * Style bits are also inherited from superclasses.
      * </p>
      *
      * @param parent a widget which will be the parent of the new instance (cannot be null)
      * @param style the style of widget to construct
      *
      * @exception IllegalArgumentException <ul>
      * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
      * </ul>
      * @exception SWTException <ul>
      * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
      * </ul>
      *
      * @see SWT#TOP
      * @see SWT#BOTTOM
      * @see SWT#FLAT
      * @see #getStyle()
      */
     public CTabFolder(Composite parent, int style) {
         super(parent, checkStyle(style));

         onBottom = (getStyle() & SWT.BOTTOM) != 0;

         borderColor1 = new Color(getDisplay(), borderInsideRGB);
         borderColor2 = new Color(getDisplay(), borderMiddleRGB);
         borderColor3 = new Color(getDisplay(), borderOutsideRGB);

         // tool tip support
 tip = new Shell(getShell(), SWT.ON_TOP);
         label = new Label(tip, SWT.CENTER);

         // Add all listeners
 Listener listener = new Listener() {
             public void handleEvent(Event event) {
                 switch (event.type) {
                 case SWT.Dispose:
                     onDispose();
                     break;
                 case SWT.Paint:
                     onPaint(event);
                     break;
                 case SWT.Resize:
                     onResize();
                     break;
                 case SWT.MouseDoubleClick:
                     onMouseDoubleClick(event);
                     break;
                 case SWT.MouseDown:
                     onMouseDown(event);
                     break;
                 case SWT.MouseExit:
                     onMouseExit(event);
                     break;
                 case SWT.MouseHover:
                     onMouseHover(event);
                     break;
                 case SWT.MouseMove:
                     onMouseMove(event);
                     break;
                 case SWT.FocusIn:
                     onFocus(event);
                     break;
                 case SWT.FocusOut:
                     onFocus(event);
                     break;
                 case SWT.KeyDown:
                     onKeyDown(event);
                     break;
                 case SWT.Traverse:
                     onTraverse(event);
                     break;
                 }
             }
         };

         int[] folderEvents = new int[] { SWT.Dispose, SWT.Paint, SWT.Resize,
                 SWT.MouseDoubleClick, SWT.MouseDown, SWT.MouseExit,
                 SWT.MouseHover, SWT.MouseMove, SWT.FocusIn, SWT.FocusOut,
                 SWT.KeyDown, SWT.Traverse, };
         for (int i = 0; i < folderEvents.length; i++) {
             addListener(folderEvents[i], listener);
         }

         createArrowBar();
         createCloseBar();

         setBorderVisible((style & SWT.BORDER) != 0);

         initAccessible();

     }

     private static int checkStyle(int style) {
         int mask = SWT.TOP | SWT.BOTTOM | SWT.FLAT | SWT.LEFT_TO_RIGHT
                 | SWT.RIGHT_TO_LEFT;
         style = style & mask;
         // TOP and BOTTOM are mutually exlusive.
 // TOP is the default
 if ((style & SWT.TOP) != 0) {
             style = style & ~(SWT.TOP | SWT.BOTTOM) | SWT.TOP;
         }
         // reduce the flash by not redrawing the entire area on a Resize event
 style |= SWT.NO_REDRAW_RESIZE;
         return style;
     }

     /**
      * Adds the listener to receive events.
      * <p>
      *
      * @param listener the listener
      *
      * @exception SWTError <ul>
      * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
      * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
      * <li>ERROR_NULL_ARGUMENT when listener is null</li>
      * </ul>
      */
     public void addSelectionListener(SelectionListener listener) {
         checkWidget();
         if (listener == null) {
             SWT.error(SWT.ERROR_NULL_ARGUMENT);
         }
         TypedListener typedListener = new TypedListener(listener);
         addListener(SWT.Selection, typedListener);
         addListener(SWT.DefaultSelection, typedListener);
     }

     /**
      * Adds the listener to the collection of listeners who will
      * be notified when a tab item is closed.
      *
      * @param listener the listener which should be notified
      *
      * @exception IllegalArgumentException <ul>
      * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
      * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
      * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
      * </ul>
      *
      * @see CTabFolderListener
      * @see #removeCTabFolderListener
      */
     public void addCTabFolderListener(CTabFolderListener listener) {
         checkWidget();
         if (listener == null) {
             SWT.error(SWT.ERROR_NULL_ARGUMENT);
         }
         // add to array
 CTabFolderListener[] newTabListeners = new CTabFolderListener[tabListeners.length + 1];
         System.arraycopy(tabListeners, 0, newTabListeners, 0,
                 tabListeners.length);
         tabListeners = newTabListeners;
         tabListeners[tabListeners.length - 1] = listener;
         showClose = true;
         setButtonBounds();
     }

     private void closeNotify(CTabItem item, int time) {
         if (item == null) {
             return;
         }

         CTabFolderEvent event = new CTabFolderEvent(this);
         event.widget = this;
         event.time = time;
         event.item = item;
         event.doit = true;
         if (tabListeners != null) {
             for (int i = 0; i < tabListeners.length; i++) {
                 tabListeners[i].itemClosed(event);
             }
         }
         if (event.doit) {
             item.dispose();
         }
     }

     public Point computeSize(int wHint, int hHint, boolean changed) {
         checkWidget();
         int minWidth = 0;
         int minHeight = 0;

         // preferred width of tab area to show all tabs
 GC gc = new GC(this);
         for (int i = 0; i < items.length; i++) {
             minWidth += items[i].preferredWidth(gc);
         }
         gc.dispose();

         // preferred size of controls in tab items
 for (int i = 0; i < items.length; i++) {
             Control control = items[i].getControl();
             if (control != null && !control.isDisposed()) {
                 Point size = control.computeSize(wHint, hHint);
                 minWidth = Math.max(minWidth, size.x);
                 minHeight = Math.max(minHeight, size.y);
             }
         }
         if (minWidth == 0) {
             minWidth = DEFAULT_WIDTH;
         }
         if (minHeight == 0) {
             minHeight = DEFAULT_HEIGHT;
         }

         if (wHint != SWT.DEFAULT) {
             minWidth = wHint;
         }
         if (hHint != SWT.DEFAULT) {
             minHeight = hHint;
         }

         Rectangle trim = computeTrim(0, 0, minWidth, minHeight);
         return new Point(trim.width, trim.height);
     }

     public Rectangle computeTrim(int x, int y, int width, int height) {
         checkWidget();
         if (items.length == 0) {
             if (!showBorders) {
                 return new Rectangle(x, y, width, height);
             }
             int trimX = x - borderRight - 1;
             int trimY = y - borderBottom - 1;
             int trimWidth = width + borderRight + 2;
             int trimHeight = height + borderBottom + 2;
             return new Rectangle(trimX, trimY, trimWidth, trimHeight);
         } else {
             int trimX = x - marginWidth - borderLeft;
             int trimY = y - marginHeight - tabHeight - borderTop - 1;
             // -1 is for the line at the bottom of the tabs
 if (onBottom) {
                 trimY = y - marginHeight - borderTop;
             }
             int trimWidth = width + borderLeft + borderRight + 2 * marginWidth;
             int trimHeight = height + borderTop + borderBottom + 2
                     * marginHeight + tabHeight + 1;
             return new Rectangle(trimX, trimY, trimWidth, trimHeight);
         }
     }

     /**
      * Create the specified item at 'index'.
      */
     void createItem(CTabItem item, int index) {
         if (0 > index || index > getItemCount()) {
             SWT.error(SWT.ERROR_INVALID_RANGE);
         }
         // grow by one and rearrange the array.
 CTabItem[] newItems = new CTabItem[items.length + 1];
         System.arraycopy(items, 0, newItems, 0, index);
         newItems[index] = item;
         System.arraycopy(items, index, newItems, index + 1, items.length
                 - index);
         items = newItems;

         item.parent = this;

         if (selectedIndex >= index) {
             selectedIndex++;
         }
         if (items.length == 1) {
             topTabIndex = 0;
             resetTabSize(true);
         } else {
             setItemBounds();
             showItem(item);
         }

         if (items.length == 1) {
             redraw();
         } else {
             redrawTabArea(-1);
         }
     }

     private void createArrowBar() {
         // create arrow buttons for scrolling
 arrowBar = new ToolBar(this, SWT.FLAT);
         arrowBar.setVisible(false);
         arrowBar.setBackground(background);
         ToolItem scrollLeft = new ToolItem(arrowBar, SWT.PUSH);
         scrollLeft.setEnabled(false);
         ToolItem scrollRight = new ToolItem(arrowBar, SWT.PUSH);
         scrollRight.setEnabled(false);

         scrollLeft.addListener(SWT.Selection, new Listener() {
             public void handleEvent(Event event) {
                 scroll_scrollLeft();
             }
         });
         scrollRight.addListener(SWT.Selection, new Listener() {
             public void handleEvent(Event event) {
                 scroll_scrollRight();
             }
         });

     }

     private void createCloseBar() {
         closeBar = new ToolBar(this, SWT.FLAT);
         closeBar.setVisible(false);
         if (gradientColors != null && gradientColors.length > 0) {
             closeBar.setBackground(gradientColors[gradientColors.length - 1]);
         } else {
             closeBar.setBackground(background);
         }
         ToolItem closeItem = new ToolItem(closeBar, SWT.PUSH);

         inactiveCloseBar = new ToolBar(this, SWT.FLAT);
         inactiveCloseBar.setVisible(false);
         inactiveCloseBar.setBackground(background);
         ToolItem inactiveCloseItem = new ToolItem(inactiveCloseBar, SWT.PUSH);

         closeItem.addListener(SWT.Selection, new Listener() {
             public void handleEvent(Event event) {
                 closeNotify(getSelection(), event.time);
             }
         });
         inactiveCloseItem.addListener(SWT.Selection, new Listener() {
             public void handleEvent(Event event) {
                 closeNotify(inactiveItem, event.time);
                 inactiveCloseBar.setVisible(false);
                 inactiveItem = null;
             }
         });
         inactiveCloseBar.addListener(SWT.MouseExit, new Listener() {
             public void handleEvent(Event event) {
                 if (inactiveItem != null) {
                     Rectangle itemBounds = inactiveItem.getBounds();
                     if (itemBounds.contains(event.x, event.y)) {
                         return;
                     }
                 }
                 inactiveCloseBar.setVisible(false);
                 inactiveItem = null;
             }
         });

     }

     /**
      * Destroy the specified item.
      */
     void destroyItem(CTabItem item) {
         if (inDispose) {
             return;
         }

         int index = indexOf(item);
         if (index == -1) {
             return; // should this trigger an error?
 }

         insertionIndex = -2;

         if (items.length == 1) {
             items = new CTabItem[0];
             selectedIndex = -1;
             topTabIndex = 0;

             Control control = item.getControl();
             if (control != null && !control.isDisposed()) {
                 control.setVisible(false);
             }
             closeBar.setVisible(false);
             if (!fixedTabHeight) {
                 tabHeight = 0;
             }
             redraw();
             return;
         }

         // shrink by one and rearrange the array.
 CTabItem[] newItems = new CTabItem[items.length - 1];
         System.arraycopy(items, 0, newItems, 0, index);
         System.arraycopy(items, index + 1, newItems, index, items.length
                 - index - 1);
         items = newItems;

         if (topTabIndex == items.length) {
             --topTabIndex;
         }

         // move the selection if this item is selected
 if (selectedIndex == index) {
             Control control = item.getControl();
             if (control != null && !control.isDisposed()) {
                 control.setVisible(false);
             }
             selectedIndex = -1;
             setSelection(Math.max(0, index - 1), true);
         } else if (selectedIndex > index) {
             selectedIndex--;
         }

         setItemBounds();
         redrawTabArea(-1);
     }

     private void onKeyDown(Event e) {
         if (e.keyCode != SWT.ARROW_LEFT && e.keyCode != SWT.ARROW_RIGHT) {
             return;
         }
         int leadKey = (getStyle() & SWT.MIRRORED) != 0 ? SWT.ARROW_RIGHT
                 : SWT.ARROW_LEFT;
         if (e.keyCode == leadKey) {
             if (selectedIndex > 0) {
                 setSelection(selectedIndex - 1, true);
             }
         } else {
             if (selectedIndex < items.length - 1) {
                 setSelection(selectedIndex + 1, true);
             }
         }
     }

     /**
      * Dispose the items of the receiver
      */
     private void onDispose() {
         /*
          * Usually when an item is disposed, destroyItem will change the size of the items array,
          * reset the bounds of all the tabs and manage the widget associated with the tab.
          * Since the whole folder is being disposed, this is not necessary. For speed
          * the inDispose flag is used to skip over this part of the item dispose.
          */
         inDispose = true;

         int length = items.length;
         for (int i = 0; i < length; i++) {
             if (items[i] != null) {
                 items[i].dispose();
             }
         }

         // clean up resources
 if (tip != null && !tip.isDisposed()) {
             tip.dispose();
             tip = null;
             label = null;
         }

         if (arrowLeftImage != null) {
             arrowLeftImage.dispose();
         }
         arrowLeftImage = null;
         if (arrowRightImage != null) {
             arrowRightImage.dispose();
         }
         arrowRightImage = null;
         if (closeImage != null) {
             closeImage.dispose();
         }
         closeImage = null;

         gradientColors = null;
         gradientPercents = null;
         backgroundImage = null;

         if (borderColor1 != null) {
             borderColor1.dispose();
         }
         borderColor1 = null;

         if (borderColor2 != null) {
             borderColor2.dispose();
         }
         borderColor2 = null;

         if (borderColor3 != null) {
             borderColor3.dispose();
         }
         borderColor3 = null;
     }

     private void onFocus(Event e) {
         checkWidget();
         if (selectedIndex >= 0) {
             redrawTabArea(selectedIndex);
         } else {
             setSelection(0, true);
         }
     }

     /**
      * Draw a border around the receiver.
      */
     private void drawBorder(GC gc) {

         Rectangle d = super.getClientArea();

         if (showBorders) {
             if ((getStyle() & SWT.FLAT) != 0) {
                 gc.setForeground(borderColor1);
                 gc.drawRectangle(d.x, d.y, d.x + d.width - 1, d.y + d.height
                         - 1);
             } else {
                 gc.setForeground(borderColor1);
                 gc.drawRectangle(d.x, d.y, d.x + d.width - 3, d.y + d.height
                         - 3);

                 gc.setForeground(borderColor2);
                 gc.drawLine(d.x + 1, d.y + d.height - 2, d.x + d.width - 1, d.y
                         + d.height - 2);
                 gc.drawLine(d.x + d.width - 2, d.y + 1, d.x + d.width - 2, d.y
                         + d.height - 1);

                 gc.setForeground(borderColor3);
                 gc.drawLine(d.x + 2, d.y + d.height - 1, d.x + d.width - 2, d.y
                         + d.height - 1);
                 gc.drawLine(d.x + d.width - 1, d.y + 2, d.x + d.width - 1, d.y
                         + d.height - 2);

                 // fill in corners with parent's background
 gc.setForeground(getParent().getBackground());
                 gc.drawLine(d.x + d.width - 2, d.y, d.x + d.width - 1, d.y);
                 gc.drawLine(d.x + d.width - 1, d.y + 1, d.x + d.width - 1,
                         d.y + 1);

                 gc.drawLine(d.x, d.y + d.height - 2, d.x, d.y + d.height - 2);
                 gc.drawLine(d.x, d.y + d.height - 1, d.x + 1, d.y + d.height
                         - 1);

                 gc.drawLine(d.x + d.width - 1, d.y + d.height - 1, d.x
                         + d.width - 1, d.y + d.height - 1);
             }

         }

         // draw a separator line
 if (items.length > 0) {
             int lineY = d.y + borderTop + tabHeight;
             if (onBottom) {
                 lineY = d.y + d.height - borderBottom - tabHeight - 1;
             }
             gc.setForeground(borderColor1);
             gc.drawLine(d.x + borderLeft, lineY, d.x + d.width - borderRight,
                     lineY);
         }

         gc.setForeground(getForeground());
     }

     public Rectangle getClientArea() {
         checkWidget();
         Point size = getSize();
         if (items.length == 0) {
             if (!showBorders) {
                 return super.getClientArea();
             }
             int width = size.x - borderRight - 2;
             int height = size.y - borderBottom - 2;
             return new Rectangle(borderRight + 1, borderBottom + 1, width,
                     height);
         } else {
             int width = size.x - 2 * marginWidth - borderLeft - borderRight;
             int height = size.y - 2 * marginHeight - borderTop - borderBottom
                     - tabHeight - 1;
             return new Rectangle(xClient, yClient, width, height);
         }
     }

     /**
      * Returns the height of the tab
      *
      * @return the height of the tab
      *
      * @exception SWTError <ul>
      * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
      * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
      * </ul>
      */
     public int getTabHeight() {
         checkWidget();
         return tabHeight;
     }

     /**
      * Return the tab that is located at the specified index.
      *
      * @return the item at the specified index
      */
     public CTabItem getItem(int index) {
         //checkWidget();
 if (index < 0 || index >= items.length) {
             SWT.error(SWT.ERROR_INVALID_RANGE);
         }
         return items[index];
     }

     /**
      * Gets the item at a point in the widget.
      * <p>
      *
      * @return the item at a point
      */
     public CTabItem getItem(Point pt) {
         //checkWidget();
 if (items.length == 0) {
             return null;
         }
         int lastItem = getLastItem();
         lastItem = Math.min(items.length - 1, lastItem + 1);
         for (int i = topTabIndex; i <= lastItem; i++) {
             Rectangle bounds = items[i].getBounds();
             if (bounds.contains(pt)) {
                 return items[i];
             }
         }
         return null;
     }

     /**
      * Return the number of tabs in the folder.
      *
      * @return the number of tabs in the folder
      */
     public int getItemCount() {
         //checkWidget();
 return items.length;
     }

     /**
      * Return the tab items.
      *
      * @return the tab items
      */
     public CTabItem[] getItems() {
         //checkWidget();
 CTabItem[] tabItems = new CTabItem[items.length];
         System.arraycopy(items, 0, tabItems, 0, items.length);
         return tabItems;
     }

     private int getLastItem() {
         if (items.length == 0) {
             return -1;
         }
         Rectangle area = getClientArea();
         if (area.width <= 0) {
             return 0;
         }
         Rectangle toolspace = getToolSpace();
         if (toolspace.width == 0) {
             return items.length - 1;
         }
         int width = area.width - toolspace.width;
         int index = topTabIndex;
         int tabWidth = items[index].width;
         while (index < items.length - 1) {
             tabWidth += items[index + 1].width;
             if (tabWidth > width) {
                 break;
             }
             index++;
         }
         return index;
     }

     /**
      * Return the selected tab item, or an empty array if there
      * is no selection.
      *
      * @return the selected tab item
      */
     public CTabItem getSelection() {
         //checkWidget();
 if (selectedIndex == -1) {
             return null;
         }
         return items[selectedIndex];
     }

     /**
      * Return the index of the selected tab item, or -1 if there
      * is no selection.
      *
      * @return the index of the selected tab item or -1
      */
     public int getSelectionIndex() {
         //checkWidget();
 return selectedIndex;
     }

     private Rectangle getToolSpace() {
         boolean showArrows = scroll_leftVisible() || scroll_rightVisible();
         if (!showArrows && topRight == null) {
             return new Rectangle(0, 0, 0, 0);
         }
         Rectangle toolspace;
         if (showArrows) {
             toolspace = arrowBar.getBounds();
             toolspace.width += borderRight;
             if (topRight != null) {
                 toolspace.width += topRight.getSize().x;
             }
         } else {
             toolspace = topRight.getBounds();
             toolspace.width += borderRight;
         }
         return toolspace;
     }

     /**
      * Returns the control in the top right corner of the tab folder.
      * Typically this is a close button or a composite with a menu and close button.
      *
      * @since 2.1
      *
      * @return the control in the top right corner of the tab folder or null
      *
      * @exception SWTError <ul>
      * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
      * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
      * </ul>
      */
     public Control getTopRight() {
         checkWidget();
         return topRight;
     }

     /**
      * Return the index of the specified tab or -1 if the tab is not
      * in the receiver.
      *
      * @return the index of the specified tab item or -1
      *
      * @exception SWTError <ul>
      * <li>ERROR_NULL_ARGUMENT when the item is null</li>
      * </ul>
      */
     public int indexOf(CTabItem item) {
         //checkWidget();
 if (item == null) {
             SWT.error(SWT.ERROR_NULL_ARGUMENT);
         }
         for (int i = 0; i < items.length; i++) {
             if (items[i] == item) {
                 return i;
             }
         }
         return -1;
     }

     private void initAccessible() {
         final Accessible accessible = getAccessible();
         accessible.addAccessibleListener(new AccessibleAdapter() {
             public void getName(AccessibleEvent e) {
                 String name = null;
                 int childID = e.childID;
                 if (childID >= 0 && childID < items.length) {
                     name = items[childID].getText();
                     int index = name.indexOf('&');
                     if (index > 0) {
                         name = name.substring(0, index)
                                 + name.substring(index + 1);
                     }
                 }
                 e.result = name;
             }

             public void getHelp(AccessibleEvent e) {
                 String help = null;
                 int childID = e.childID;
                 if (childID == ACC.CHILDID_SELF) {
                     help = getToolTipText();
                 } else if (childID >= 0 && childID < items.length) {
                     help = items[childID].getToolTipText();
                 }
                 e.result = help;
             }

             public void getKeyboardShortcut(AccessibleEvent e) {
                 String shortcut = null;
                 int childID = e.childID;
                 if (childID >= 0 && childID < items.length) {
                     String text = items[childID].getText();
                     if (text != null) {
                         char mnemonic = getMnemonic(text);
                         if (mnemonic != '\0') {
                             shortcut = "Alt+" + mnemonic; //$NON-NLS-1$
 }
                    }
                }
                e.result = shortcut;
            }
        });

        accessible.addAccessibleControlListener(new AccessibleControlAdapter() {
            public void getChildAtPoint(AccessibleControlEvent e) {
                Point testPoint = toControl(new Point(e.x, e.y));
                int childID = ACC.CHILDID_NONE;
                for (int i = 0; i < items.length; i++) {
                    if (items[i].getBounds().contains(testPoint)) {
                        childID = i;
                        break;
                    }
                }
                if (childID == ACC.CHILDID_NONE) {
                    Rectangle location = getBounds();
                    location.height = location.height - getClientArea().height;
                    if (location.contains(testPoint)) {
                        childID = ACC.CHILDID_SELF;
                    }
                }
                e.childID = childID;
            }

            public void getLocation(AccessibleControlEvent e) {
                Rectangle location = null;
                int childID = e.childID;
                if (childID == ACC.CHILDID_SELF) {
                    location = getBounds();
                }
                if (childID >= 0 && childID < items.length) {
                    location = items[childID].getBounds();
                }
                if (location != null) {
                    Point pt = toDisplay(new Point(location.x, location.y));
                    e.x = pt.x;
                    e.y = pt.y;
                    e.width = location.width;
                    e.height = location.height;
                }
            }

            public void getChildCount(AccessibleControlEvent e) {
                e.detail = items.length;
            }

            public void getDefaultAction(AccessibleControlEvent e) {
                String action = null;
                int childID = e.childID;
                if (childID >= 0 && childID < items.length) {
                    action = "Switch"; //$NON-NLS-1$
 }
                e.result = action;
            }

            public void getFocus(AccessibleControlEvent e) {
                int childID = ACC.CHILDID_NONE;
                if (isFocusControl()) {
                    if (selectedIndex == -1) {
                        childID = ACC.CHILDID_SELF;
                    } else {
                        childID = selectedIndex;
                    }
                }
                e.childID = childID;
            }

            public void getRole(AccessibleControlEvent e) {
                int role = 0;
                int childID = e.childID;
                if (childID == ACC.CHILDID_SELF) {
                    role = ACC.ROLE_TABFOLDER;
                } else if (childID >= 0 && childID < items.length) {
                    role = ACC.ROLE_TABITEM;
                }
                e.detail = role;
            }

            public void getSelection(AccessibleControlEvent e) {
                e.childID = (selectedIndex == -1) ? ACC.CHILDID_NONE
                        : selectedIndex;
            }

            public void getState(AccessibleControlEvent e) {
                int state = 0;
                int childID = e.childID;
                if (childID == ACC.CHILDID_SELF) {
                    state = ACC.STATE_NORMAL;
                } else if (childID >= 0 && childID < items.length) {
                    state = ACC.STATE_SELECTABLE;
                    if (isFocusControl()) {
                        state |= ACC.STATE_FOCUSABLE;
                    }
                    if (selectedIndex == childID) {
                        state |= ACC.STATE_SELECTED;
                        if (isFocusControl()) {
                            state |= ACC.STATE_FOCUSED;
                        }
                    }
                }
                e.detail = state;
            }

            public void getChildren(AccessibleControlEvent e) {
                Object [] children = new Object [items.length];
                for (int i = 0; i < items.length; i++) {
                    children[i] = new Integer (i);
                }
                e.children = children;
            }
        });

        addListener(SWT.Selection, new Listener() {
            public void handleEvent(Event event) {
                if (isFocusControl()) {
                    if (selectedIndex == -1) {
                        accessible.setFocus(ACC.CHILDID_SELF);
                    } else {
                        accessible.setFocus(selectedIndex);
                    }
                }
            }
        });

        addListener(SWT.FocusIn, new Listener() {
            public void handleEvent(Event event) {
                if (selectedIndex == -1) {
                    accessible.setFocus(ACC.CHILDID_SELF);
                } else {
                    accessible.setFocus(selectedIndex);
                }
            }
        });
    }

    private void setButtonBounds() {

        updateArrowBar();
        updateCloseBar();

        Rectangle area = super.getClientArea();

        int offset = 0;
        if (topRight != null) {
            Point size = topRight.computeSize(SWT.DEFAULT, tabHeight);
            int x = area.x + area.width - borderRight - size.x;
            int y = onBottom ? area.y + area.height - borderBottom - size.y
                    : area.y + borderTop;
            topRight.setBounds(x, y, size.x, size.y);
            offset = size.x;
        }
        boolean leftVisible = scroll_leftVisible();
        boolean rightVisible = scroll_rightVisible();
        if (leftVisible || rightVisible) {
            Point size = arrowBar.computeSize(SWT.DEFAULT, tabHeight);
            int x = area.x + area.width - borderRight - size.x - offset;
            int y = (onBottom) ? area.y + area.height - borderBottom - size.y
                    : area.y + borderTop;

            arrowBar.setBounds(x, y, size.x, size.y);
            ToolItem[] items = arrowBar.getItems();
            items[0].setEnabled(leftVisible);
            items[1].setEnabled(rightVisible);
            arrowBar.setVisible(true);
        } else {
            arrowBar.setVisible(false);
        }

        // When the close button is right at the edge of the Tab folder, hide it because
 // otherwise it may block off a part of the border on the right
 if (showClose) {
            inactiveCloseBar.setVisible(false);
            CTabItem item = getSelection();
            if (item == null) {
                closeBar.setVisible(false);
            } else {
                int toolbarHeight = tabHeight - CTabItem.TOP_MARGIN
                        - CTabItem.BOTTOM_MARGIN + 2; // +2 to ignore gap between focus rectangle
 Point size = closeBar.computeSize(SWT.DEFAULT, toolbarHeight);
                int x = item.x + item.width - size.x - 2; // -2 to not overlap focus rectangle and trim
 int y = item.y + Math.max(0, (item.height - toolbarHeight) / 2);
                closeBar.setBounds(x, y, size.x, toolbarHeight);
                Rectangle toolspace = getToolSpace();
                Point folderSize = getSize();
                boolean visible = (toolspace.width == 0 || x < toolspace.x)
                        && x + size.x < folderSize.x - borderRight;
                closeBar.setVisible(visible);
            }
        }
    }

    private boolean setItemLocation() {
        if (items.length == 0) {
            return false;
        }
        Rectangle area = super.getClientArea();
        int x = area.x;
        int y = area.y + borderTop;
        if (onBottom) {
            y = Math.max(0, area.y + area.height - borderBottom - tabHeight);
        }

        boolean changed = false;
        for (int i = topTabIndex - 1; i >= 0; i--) {
            // if the first visible tab is not the first tab
 CTabItem tab = items[i];
            x -= tab.width;
            if (!changed && (tab.x != x || tab.y != y)) {
                changed = true;
            }
            // layout tab items from right to left thus making them invisible
 tab.x = x;
            tab.y = y;
        }

        x = area.x + borderLeft;
        for (int i = topTabIndex; i < items.length; i++) {
            // continue laying out remaining, visible items left to right
 CTabItem tab = items[i];
            tab.x = x;
            tab.y = y;
            x = x + tab.width;
        }
        setButtonBounds();
        return changed;
    }

    private void setLastItem(int index) {
        if (index < 0 || index > items.length - 1) {
            return;
        }
        Rectangle area = getClientArea();
        if (area.width <= 0) {
            return;
        }
        int maxWidth = area.width;
        Rectangle toolspace = getToolSpace();
        if (toolspace.width > 0) {
            maxWidth -= toolspace.width;
        }
        int tabWidth = items[index].width;
        while (index > 0) {
            tabWidth += items[index - 1].width;
            if (tabWidth > maxWidth) {
                break;
            }
            index--;
        }
        topTabIndex = index;
        setItemLocation();
        redrawTabArea(-1);
    }

    /**
     * Layout the items and store the client area size.
     */
    boolean setItemBounds() {
        boolean changed = false;
        if (isDisposed()) {
            return changed;
        }
        Rectangle area = super.getClientArea();

        xClient = area.x + borderLeft + marginWidth;
        if (onBottom) {
            yClient = area.y + borderTop + marginHeight;
        } else {
            yClient = area.y + borderTop + tabHeight + 1 + marginHeight;
            // +1 is for the line at the bottom of the tabs
 }

        if (area.width <= 0 || area.height <= 0 || items.length == 0) {
            return changed;
        }

        int[] widths = new int[items.length];
        GC gc = new GC(this);
        for (int i = 0; i < items.length; i++) {
            widths[i] = items[i].preferredWidth(gc);
        }
        gc.dispose();

        int oldAverageWidth = 0;
        int averageWidth = (area.width - borderLeft - borderRight)
                / items.length;
        while (averageWidth > oldAverageWidth) {
            int width = area.width - borderLeft - borderRight;
            int count = items.length;
            for (int i = 0; i < items.length; i++) {
                if (widths[i] < averageWidth) {
                    width -= widths[i];
                    count--;
                }
            }
            oldAverageWidth = averageWidth;
            if (count > 0) {
                averageWidth = width / count;
            }
        }
        averageWidth = Math.max(averageWidth, MIN_TAB_WIDTH * tabHeight);
        for (int i = 0; i < items.length; i++) {
            if (widths[i] > averageWidth) {
                widths[i] = averageWidth;
            }
        }

        int totalWidth = 0;
        for (int i = 0; i < items.length; i++) {
            CTabItem tab = items[i];
            if (tab.height != tabHeight || tab.width != widths[i]) {
                changed = true;
            }
            tab.height = tabHeight;
            tab.width = widths[i];
            totalWidth += widths[i];
        }

        int areaWidth = area.x + area.width - borderRight;
        if (totalWidth <= areaWidth) {
            topTabIndex = 0;
        }
        if (setItemLocation()) {
            changed = true;
        }

        // Is there a gap after last item showing
 if (correctLastItem()) {
            changed = true;
        }
        return changed;
    }

    private boolean onMnemonic(Event event) {
        char key = event.character;
        for (int i = 0; i < items.length; i++) {
            if (items[i] != null) {
                char mnemonic = getMnemonic(items[i].getText());
                if (mnemonic != '\0') {
                    if (Character.toUpperCase(key) == Character
                            .toUpperCase(mnemonic)) {
                        setSelection(i, true);
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * Paint the receiver.
     */
    private void onPaint(Event event) {
        Font font = getFont();
        if (oldFont == null || !oldFont.equals(font)) {
            oldFont = font;
            resetTabSize(true);
        }
        GC gc = event.gc;
        Rectangle rect = super.getClientArea();
        if (items.length == 0) {
            if (showBorders) {
                if ((getStyle() & SWT.FLAT) != 0) {
                    gc.setForeground(borderColor1);
                    gc.drawRectangle(rect.x, rect.y, rect.x + rect.width - 1,
                            rect.y + rect.height - 1);
                } else {
                    gc.setForeground(borderColor1);
                    gc.drawRectangle(rect.x, rect.y, rect.x + rect.width - 3,
                            rect.y + rect.height - 3);

                    // fill in right and bottom edges with parent's background
 gc.setBackground(getParent().getBackground());
                    gc.fillRectangle(rect.x + rect.width - 2, rect.y, 2,
                            rect.height);
                    gc.fillRectangle(rect.x, rect.y + rect.height - 2,
                            rect.width, 2);
                }
                gc.setForeground(getForeground());
            }
            return;
        }

        // redraw the Border
 drawBorder(gc);

        rect.x += borderLeft;
        rect.y += borderTop;
        rect.width -= borderLeft + borderRight;
        rect.height -= borderTop + borderBottom;
        Rectangle clip = gc.getClipping();
        gc.setClipping(clip.intersection(rect));

        // Draw the unselected tabs first.
 for (int i = 0; i < items.length; i++) {
            if (i != selectedIndex
                    && event.getBounds().intersects(items[i].getBounds())) {
                items[i].onPaint(gc, false);
            }
        }
        // Selected tab comes last
 if (selectedIndex != -1) {
            items[selectedIndex].onPaint(gc, true);
        }

        // draw insertion mark
 if (insertionIndex > -2) {
            gc.setForeground(getDisplay().getSystemColor(
                    SWT.COLOR_LIST_SELECTION));
            if (insertionIndex == -1) {
                Rectangle bounds = items[0].getBounds();
                gc.drawLine(bounds.x, bounds.y, bounds.x, bounds.y
                        + bounds.height - 1);
                gc.drawLine(bounds.x - 2, bounds.y, bounds.x + 2, bounds.y);
                gc.drawLine(bounds.x - 1, bounds.y + 1, bounds.x + 1,
                        bounds.y + 1);
                gc.drawLine(bounds.x - 1, bounds.y + bounds.height - 2,
                        bounds.x + 1, bounds.y + bounds.height - 2);
                gc.drawLine(bounds.x - 2, bounds.y + bounds.height - 1,
                        bounds.x + 2, bounds.y + bounds.height - 1);

            } else {
                Rectangle bounds = items[insertionIndex].getBounds();
                gc.drawLine(bounds.x + bounds.width, bounds.y, bounds.x
                        + bounds.width, bounds.y + bounds.height - 1);
                gc.drawLine(bounds.x + bounds.width - 2, bounds.y, bounds.x
                        + bounds.width + 2, bounds.y);
                gc.drawLine(bounds.x + bounds.width - 1, bounds.y + 1, bounds.x
                        + bounds.width + 1, bounds.y + 1);
                gc.drawLine(bounds.x + bounds.width - 1, bounds.y
                        + bounds.height - 2, bounds.x + bounds.width + 1,
                        bounds.y + bounds.height - 2);
                gc.drawLine(bounds.x + bounds.width - 2, bounds.y
                        + bounds.height - 1, bounds.x + bounds.width + 2,
                        bounds.y + bounds.height - 1);
            }
        }

        gc.setForeground(getForeground());
        gc.setBackground(getBackground());
    }

    private void redrawTabArea(int index) {
        int x = 0, y = 0, width = 0, height = 0;
        if (index == -1) {
            Rectangle area = super.getClientArea();
            if (area.width == 0 || area.height == 0) {
                return;
            }
            width = area.x + area.width - borderLeft - borderRight;
            height = tabHeight + 1; // +1 causes top line between content and tabs to be redrawn
 x = area.x + borderLeft;
            y = area.y + borderTop;
            if (onBottom) {
                y = Math.max(0, area.y + area.height - borderBottom - height);
            }
        } else {
            CTabItem item = items[index];
            x = item.x;
            y = item.y;
            Rectangle area = super.getClientArea();
            width = area.x + area.width - x;
            height = item.height;
        }
        redraw(x, y, width, height, false);
    }

    /**
     * Removes the listener.
     *
     * @param listener the listener
     *
     * @exception SWTError
     * <ul><li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * <li>ERROR_NULL_ARGUMENT when listener is null</li></ul>
     */
    public void removeSelectionListener(SelectionListener listener) {
        checkWidget();
        if (listener == null) {
            SWT.error(SWT.ERROR_NULL_ARGUMENT);
        }
        removeListener(SWT.Selection, listener);
        removeListener(SWT.DefaultSelection, listener);
    }

    /**
     * Removes the listener.
     *
     * @param listener the listener
     *
     * @exception SWTError
     * <ul><li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * <li>ERROR_NULL_ARGUMENT when listener is null</li></ul>
     */
    public void removeCTabFolderListener(CTabFolderListener listener) {
        checkWidget();
        if (listener == null) {
            SWT.error(SWT.ERROR_NULL_ARGUMENT);
        }
        if (tabListeners.length == 0) {
            return;
        }
        int index = -1;
        for (int i = 0; i < tabListeners.length; i++) {
            if (listener == tabListeners[i]) {
                index = i;
                break;
            }
        }
        if (index == -1) {
            return;
        }
        if (tabListeners.length == 1) {
            tabListeners = new CTabFolderListener[0];
            showClose = false;
            setButtonBounds();
            return;
        }
        CTabFolderListener[] newTabListeners = new CTabFolderListener[tabListeners.length - 1];
        System.arraycopy(tabListeners, 0, newTabListeners, 0, index);
        System.arraycopy(tabListeners, index + 1, newTabListeners, index,
                tabListeners.length - index - 1);
        tabListeners = newTabListeners;
    }

    /**
     * The widget was resized. Adjust the size of the currently selected page.
     */
    private void onResize() {

        if (items.length == 0) {
            redraw();
            return;
        }

        if (setItemBounds()) {
            redrawTabArea(-1);
        }

        Point size = getSize();
        if (oldSize == null) {
            redraw();
        } else {
            if (onBottom && size.y != oldSize.y) {
                redraw();
            } else {
                int x1 = Math.min(size.x, oldSize.x);
                if (size.x != oldSize.x) {
                    x1 -= 10;
                }
                int y1 = Math.min(size.y, oldSize.y);
                if (size.y != oldSize.y) {
                    y1 -= 10;
                }
                int x2 = Math.max(size.x, oldSize.x);
                int y2 = Math.max(size.y, oldSize.y);
                redraw(0, y1, x2 + 10, y2 - y1, false);
                redraw(x1, 0, x2 - x1, y2, false);
            }
        }
        oldSize = size;

        // resize content
 if (selectedIndex != -1) {
            Control control = items[selectedIndex].getControl();
            if (control != null && !control.isDisposed()) {
                control.setBounds(getClientArea());
            }
        }
    }

    public void setBackground(Color color) {
        super.setBackground(color);
        background = color;
        // init inactive close button
 inactiveCloseBar.setBackground(color);

        // init scroll buttons
 arrowBar.setBackground(color);

        //init topRight control
 if (topRight != null) {
            topRight.setBackground(color);
        }

        // init close button
 if (gradientColors == null) {
            closeBar.setBackground(color);
        }
    }

    /**
     * Specify a gradient of colours to be draw in the background of the selected tab.
     * For example to draw a gradient that varies from dark blue to blue and then to
     * white, use the following call to setBackground:
     * <pre>
     * cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
     * display.getSystemColor(SWT.COLOR_BLUE),
     * display.getSystemColor(SWT.COLOR_WHITE),
     * display.getSystemColor(SWT.COLOR_WHITE)},
     * new int[] {25, 50, 100});
     * </pre>
     *
     * @param colors an array of Color that specifies the colors to appear in the gradient
     * in order of appearance left to right. The value <code>null</code> clears the
     * background gradient. The value <code>null</code> can be used inside the array of
     * Color to specify the background color.
     * @param percents an array of integers between 0 and 100 specifying the percent of the width
     * of the widget at which the color should change. The size of the percents array must be one
     * less than the size of the colors array.
     *
     * @exception SWTError <ul>
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * </ul>
     */

    public void setSelectionBackground(Color[] colors, int[] percents) {
        checkWidget();
        if (colors != null) {
            if (percents == null || percents.length != colors.length - 1) {
                SWT.error(SWT.ERROR_INVALID_ARGUMENT);
            }
            if (getDisplay().getDepth() < 15) {
                // Don't use gradients on low color displays
 colors = new Color[] { colors[0] };
                percents = new int[] {};
            }
            for (int i = 0; i < percents.length; i++) {
                if (percents[i] < 0 || percents[i] > 100) {
                    SWT.error(SWT.ERROR_INVALID_ARGUMENT);
                }
                if (i > 0 && percents[i] < percents[i - 1]) {
                    SWT.error(SWT.ERROR_INVALID_ARGUMENT);
                }
            }
        }

        // Are these settings the same as before?
 if (backgroundImage == null) {
            if ((gradientColors != null) && (colors != null)
                    && (gradientColors.length == colors.length)) {
                boolean same = false;
                for (int i = 0; i < gradientColors.length; i++) {
                    if (gradientColors[i] == null) {
                        same = colors[i] == null;
                    } else {
                        same = gradientColors[i].equals(colors[i]);
                    }
                    if (!same) {
                        break;
                    }
                }
                if (same) {
                    for (int i = 0; i < gradientPercents.length; i++) {
                        same = gradientPercents[i] == percents[i];
                        if (!same) {
                            break;
                        }
                    }
                }
                if (same) {
                    return;
                }
            }
        } else {
            backgroundImage = null;
        }
        // Store the new settings
 if (colors == null) {
            gradientColors = null;
            gradientPercents = null;
            closeBar.setBackground(background);
        } else {
            gradientColors = new Color[colors.length];
            for (int i = 0; i < colors.length; ++i) {
                gradientColors[i] = colors[i];
            }
            gradientPercents = new int[percents.length];
            for (int i = 0; i < percents.length; ++i) {
                gradientPercents[i] = percents[i];
            }
            if (getDisplay().getDepth() < 15) {
                closeBar.setBackground(background);
            } else {
                closeBar
                        .setBackground(gradientColors[gradientColors.length - 1]);
            }
        }

        // Refresh with the new settings
 if (selectedIndex > -1) {
            redrawTabArea(selectedIndex);
        }
    }

    /**
     * Set the image to be drawn in the background of the selected tab.
     *
     * @param image the image to be drawn in the background
     *
     * @exception SWTException <ul>
     * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     */
    public void setSelectionBackground(Image image) {
        checkWidget();
        if (image == backgroundImage) {
            return;
        }
        if (image != null) {
            gradientColors = null;
            gradientPercents = null;
        }
        backgroundImage = image;
        redrawTabArea(selectedIndex);
    }

    /**
     * Toggle the visibility of the border
     *
     * @param show true if the border should be displayed
     *
     * @exception SWTException <ul>
     * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     */
    public void setBorderVisible(boolean show) {
        checkWidget();
        // if (showBorders == show) return;

        showBorders = show;
        if (showBorders) {
            if ((getStyle() & SWT.FLAT) != 0) {
                borderBottom = borderTop = borderLeft = borderRight = 1;
            } else {
                borderLeft = borderTop = 1;
                borderRight = borderBottom = 3;
            }
        } else {
            borderBottom = borderTop = borderLeft = borderRight = 0;
        }
        oldSize = null;
        notifyListeners(SWT.Resize, new Event());
    }

    public void setFont(Font font) {
        checkWidget();
        if (font != null && font.equals(getFont())) {
            return;
        }
        super.setFont(font);
        oldFont = getFont();
        resetTabSize(true);
    }

    /**
     * Set the foreground color of the selected tab.
     *
     * @param color the color of the text displayed in the selected tab
     *
     * @exception SWTException <ul>
     * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     */
    public void setSelectionForeground(Color color) {
        checkWidget();
        if (selectionForeground == color) {
            return;
        }
        if (color == null) {
            color = getForeground();
        }
        selectionForeground = color;
        if (selectedIndex > -1) {
            redrawTabArea(selectedIndex);
        }
    }

    /**
     * Display an insert marker before or after the specified tab item.
     *
     * A value of null will clear the mark.
     *
     * @param item the item with which the mark is associated or null
     *
     * @param after true if the mark should be displayed after the specified item
     *
     * @exception SWTException <ul>
     * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     */
    public void setInsertMark(CTabItem item, boolean after) {
        checkWidget();
        int index = -1;
        if (item != null) {
            index = indexOf(item);
        }
        setInsertMark(index, after);
    }

    /**
     * Display an insert marker before or after the specified tab item.
     *
     * A value of -1 will clear the mark.
     *
     * @param index the index of the item with which the mark is associated or null
     *
     * @param after true if the mark should be displayed after the specified item
     *
     * @exception SWTException <ul>
     * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     */
    public void setInsertMark(int index, boolean after) {
        checkWidget();
        if (index < -1 || index >= getItemCount()) {
            SWT.error(SWT.ERROR_INVALID_ARGUMENT);
        }

        if (index == -1) {
            index = -2;
        } else {
            index = after ? index : --index;
        }

        if (insertionIndex == index) {
            return;
        }
        int oldIndex = insertionIndex;
        insertionIndex = index;
        if (index > -1) {
            redrawTabArea(index);
        }
        if (oldIndex > 1) {
            redrawTabArea(oldIndex);
        }
    }

    /**
     * Set the selection to the tab at the specified index.
     *
     * @param index the index of the tab item to be selected
     *
     * @exception SWTException <ul>
     * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     */
    public void setSelection(int index) {
        checkWidget();
        if (index < 0 || index >= items.length) {
            return;
        }
        if (selectedIndex == index) {
            return;
        }

        int oldIndex = selectedIndex;
        selectedIndex = index;

        Control control = items[index].control;
        if (control != null && !control.isDisposed()) {
            control.setBounds(getClientArea());
            control.setVisible(true);
        }

        if (oldIndex != -1) {
            control = items[oldIndex].control;
            if (control != null && !control.isDisposed()) {
                control.setVisible(false);
            }
        }
        showItem(items[selectedIndex]);
        setButtonBounds();
        redrawTabArea(-1);
    }

    /**
     * Set the control that appears in the top right corner of the tab folder.
     * Typically this is a close button or a composite with a Menu and close button.
     * The topRight control is optional. Setting the top right control to null will remove it from the tab folder.
     *
     * @since 2.1
     *
     * @param control the control to be displayed in the top right corner or null
     *
     * @exception SWTException <ul>
     * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * <li>ERROR_INVALID_ARGUMENT - if the control is not a child of this CTabFolder</li>
     * </ul>
     */
    public void setTopRight(Control control) {
        checkWidget();
        if (control != null && control.getParent() != this) {
            SWT.error(SWT.ERROR_INVALID_ARGUMENT);
        }
        topRight = control;
        resetTabSize(true);
    }

    /**
     * Shows the item. If the item is already showing in the receiver,
     * this method simply returns. Otherwise, the items are scrolled until
     * the item is visible.
     *
     * @param item the item to be shown
     *
     * @exception IllegalArgumentException <ul>
     * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
     * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
     * </ul>
     * @exception SWTException <ul>
     * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @see CTabFolder#showSelection()
     *
     * @since 2.0
     */
    public void showItem(CTabItem item) {
        checkWidget();
        if (item == null) {
            SWT.error(SWT.ERROR_NULL_ARGUMENT);
        }
        if (item.isDisposed()) {
            SWT.error(SWT.ERROR_INVALID_ARGUMENT);
        }

        int index = indexOf(item);
        if (index < topTabIndex) {
            topTabIndex = index;
            setItemLocation();
            redrawTabArea(-1);
            return;
        }
        Rectangle area = getClientArea();
        if (area.width <= 0) {
            topTabIndex = index;
            return;
        }
        int rightEdge = area.x + area.width;
        Rectangle rect = getToolSpace();
        if (rect.width > 0) {
            rightEdge -= rect.width;
        }
        if (item.x + item.width < rightEdge) {
            return;
        }
        setLastItem(index);
    }

    /**
     * Shows the selection. If the selection is already showing in the receiver,
     * this method simply returns. Otherwise, the items are scrolled until
     * the selection is visible.
     *
     * @exception IllegalArgumentException <ul>
     * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
     * </ul>
     * @exception SWTException <ul>
     * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @see CTabFolder#showItem(CTabItem)
     *
     * @since 2.0
     *
     */
    public void showSelection() {
        checkWidget();
        if (selectedIndex != -1) {
            showItem(getSelection());
        }
    }

    char getMnemonic(String string) {
        int index = 0;
        int length = string.length();
        do {
            while ((index < length) && (string.charAt(index) != '&')) {
                index++;
            }
            if (++index >= length) {
                return '\0';
            }
            if (string.charAt(index) != '&') {
                return string.charAt(index);
            }
            index++;
        } while (index < length);
        return '\0';
    }

    /**
     * Set the selection to the tab at the specified item.
     *
     * @param item the tab item to be selected
     *
     * @exception SWTException <ul>
     * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * <li>ERROR_NULL_ARGUMENT - if argument is null</li>
     * </ul>
     */
    public void setSelection(CTabItem item) {
        checkWidget();
        if (item == null) {
            SWT.error(SWT.ERROR_NULL_ARGUMENT);
        }
        int index = indexOf(item);
        setSelection(index);
    }

    /**
     * Set the selection to the tab at the specified index.
     */
    private void setSelection(int index, boolean notify) {
        int oldSelectedIndex = selectedIndex;
        setSelection(index);
        if (notify && selectedIndex != oldSelectedIndex && selectedIndex != -1) {
            Event event = new Event();
            event.item = getItem(selectedIndex);
            notifyListeners(SWT.Selection, event);
        }
    }

    private Image scaleImage(Image image, int oldSize, int newSize) {
        Display display = getDisplay();
        Color foreground = getForeground();
        Color black = display.getSystemColor(SWT.COLOR_BLACK);
        Color background = getBackground();
        PaletteData palette = new PaletteData(new RGB[] { foreground.getRGB(),
                background.getRGB(), black.getRGB() });
        ImageData imageData = new ImageData(newSize, newSize, 4, palette);
        imageData.transparentPixel = 1;
        Image temp = new Image(display, imageData);
        GC gc = new GC(temp);
        gc.setBackground(background);
        gc.fillRectangle(0, 0, newSize, newSize);
        gc.drawImage(image, 0, 0, oldSize, oldSize, 0, 0, newSize, newSize);
        gc.dispose();
        return temp;
    }

    private void updateCloseBar() {
        //Temporary code - need a better way to determine toolBar trim
 int toolbarTrim = 4;
        String platform = SWT.getPlatform();
        if ("photon".equals(platform)) { //$NON-NLS-1$
 toolbarTrim = 6;
        }
        if ("gtk".equals(platform)) { //$NON-NLS-1$
 toolbarTrim = 8;
        }

        int maxHeight = tabHeight - CTabItem.TOP_MARGIN
                - CTabItem.BOTTOM_MARGIN - toolbarTrim;
        if (maxHeight < 3) {
            return;
        }
        int imageHeight = (maxHeight < 9) ? 9 : maxHeight;

        if (closeImage != null && closeImage.getBounds().height == imageHeight) {
            return;
        }

        if (closeBar != null) {
            closeBar.dispose();
        }
        closeBar = null;
        if (inactiveCloseBar != null) {
            inactiveCloseBar.dispose();
        }
        inactiveCloseBar = null;
        createCloseBar();

        ToolItem closeItem = closeBar.getItems()[0];
        ToolItem inactiveCloseItem = inactiveCloseBar.getItems()[0];

        if (closeImage != null) {
            closeImage.dispose();
        }

        Display display = getDisplay();
        Color foreground = getForeground();
        Color black = display.getSystemColor(SWT.COLOR_BLACK);
        Color background = getBackground();

        PaletteData palette = new PaletteData(new RGB[] { foreground.getRGB(),
                background.getRGB(), black.getRGB() });
        ImageData imageData = new ImageData(imageHeight, imageHeight, 4,
                palette);
        imageData.transparentPixel = 1;
        closeImage = new Image(display, imageData);
        GC gc = new GC(closeImage);
        gc.setBackground(background);
        gc.fillRectangle(0, 0, imageHeight, imageHeight);
        gc.setForeground(black);

        //draw an 9x8 'x' centered in image
 int h = (imageHeight / 2) * 2;
        int inset = (h - 8) / 2;
        gc.drawLine(inset, inset, h - inset - 1, h - inset - 1);
        gc.drawLine(inset + 1, inset, h - inset, h - inset - 1);
        gc.drawLine(inset, h - inset - 1, h - inset - 1, inset);
        gc.drawLine(inset + 1, h - inset - 1, h - inset, inset);

        gc.dispose();

        if (maxHeight < imageHeight) {
            //rescale image
 Image temp = scaleImage(closeImage, imageHeight, maxHeight);
            closeImage.dispose();
            closeImage = temp;
        }
        closeItem.setImage(closeImage);
        inactiveCloseItem.setImage(closeImage);
    }

    private void updateArrowBar() {
        //Temporary code - need a better way to determine toolBar trim
 int toolbarTrim = 6; // Windows needs 6, photon needs 6, gtk needs 8
 String platform = SWT.getPlatform();
        if ("gtk".equals(platform)) { //$NON-NLS-1$
 toolbarTrim = 8;
        }

        int maxHeight = tabHeight - toolbarTrim;
        if (maxHeight < 3) {
            return;
        }
        int imageHeight = (maxHeight < 9) ? 9 : maxHeight;

        if (arrowLeftImage != null
                && arrowLeftImage.getBounds().height == imageHeight) {
            return;
        }

        if (arrowBar != null) {
            arrowBar.dispose();
        }
        arrowBar = null;
        if (arrowLeftImage != null) {
            arrowLeftImage.dispose();
        }
        if (arrowRightImage != null) {
            arrowRightImage.dispose();
        }

        createArrowBar();
        ToolItem[] items = arrowBar.getItems();
        ToolItem left = items[0];
        ToolItem right = items[1];

        Display display = getDisplay();
        Color foreground = getForeground();
        Color black = display.getSystemColor(SWT.COLOR_BLACK);
        Color background = getBackground();

        PaletteData palette = new PaletteData(new RGB[] { foreground.getRGB(),
                background.getRGB(), black.getRGB() });
        ImageData imageData = new ImageData(7, imageHeight, 4, palette);
        imageData.transparentPixel = 1;
        arrowLeftImage = new Image(display, imageData);
        GC gc = new GC(arrowLeftImage);
        gc.setBackground(background);
        gc.fillRectangle(0, 0, 7, imageHeight);
        gc.setBackground(black);
        //draw a 9x5 '<' centered vertically in image
 int h = (imageHeight / 2) * 2;
        int midpoint = h / 2 - 1;
        int[] pointArr = new int[] { 6, midpoint - 5, 1, midpoint, 6,
                midpoint + 5, };
        gc.fillPolygon(pointArr);
        gc.dispose();

        palette = new PaletteData(new RGB[] { foreground.getRGB(),
                background.getRGB(), black.getRGB() });
        imageData = new ImageData(7, imageHeight, 4, palette);
        imageData.transparentPixel = 1;
        arrowRightImage = new Image(display, imageData);
        gc = new GC(arrowRightImage);
        gc.setBackground(background);
        gc.fillRectangle(0, 0, 7, imageHeight);
        gc.setBackground(black);
        //draw a 9x5 '>' centered vertically in image
 pointArr = new int[] { 1, midpoint - 5, 6, midpoint, 1, midpoint + 5, };
        gc.fillPolygon(pointArr);
        gc.dispose();

        if (maxHeight < imageHeight) {
            //rescale image
 Image leftTemp = scaleImage(arrowLeftImage, imageHeight, maxHeight);
            arrowLeftImage.dispose();
            arrowLeftImage = leftTemp;

            Image rightTemp = scaleImage(arrowRightImage, imageHeight,
                    maxHeight);
            arrowRightImage.dispose();
            arrowRightImage = rightTemp;
        }
        left.setImage(arrowLeftImage);
        right.setImage(arrowRightImage);
    }

    private void onMouseDoubleClick(Event event) {
        Event e = new Event();
        e.item = getItem(new Point(event.x, event.y));
        notifyListeners(SWT.DefaultSelection, e);
    }

    /**
     * A mouse button was pressed down.
     * If a tab was hit select the tab.
     */
    private void onMouseDown(Event event) {
        for (int i = 0; i < items.length; i++) {
            if (items[i].getBounds().contains(new Point(event.x, event.y))) {
                if (i == selectedIndex) {
                    showSelection();
                    return;
                }
                forceFocus();
                setSelection(i, true);
                if (isFocusControl()) {
                    setFocus();
                }
                return;
            }
        }
    }

    private void onMouseExit(Event event) {
        Rectangle inactiveBounds = inactiveCloseBar.getBounds();
        if (inactiveBounds.contains(event.x, event.y)) {
            return;
        }
        inactiveCloseBar.setVisible(false);
        inactiveItem = null;

        showToolTip = false;
        toolTipItem = null;
        if (tip != null && !tip.isDisposed() && tip.isVisible()) {
            tip.setVisible(false);
        }
    }

    private void onMouseHover(Event event) {
        if (tip == null || tip.isDisposed()) {
            return;
        }
        showToolTip = true;
        showToolTip(event.x, event.y);
    }

    private void showToolTip(int x, int y) {
        CTabItem item = getItem(new Point(x, y));
        if (item != null) {
            if (item == toolTipItem) {
                return;
            }
            toolTipItem = item;
            String tooltip = item.getToolTipText();
            if (tooltip != null && tooltip.length() > 0) {
                Display display = tip.getDisplay();
                label.setForeground(display
                        .getSystemColor(SWT.COLOR_INFO_FOREGROUND));
                label.setBackground(display
                        .getSystemColor(SWT.COLOR_INFO_BACKGROUND));
                label.setText(tooltip);
                Point labelSize = label.computeSize(SWT.DEFAULT, SWT.DEFAULT);
                labelSize.x += 2;
                labelSize.y += 2;
                label.setSize(labelSize);
                tip.pack();
                /*
                 * On some platforms, there is a minimum size for a shell
                 * which may be greater than the label size.
                 * To avoid having the background of the tip shell showing
                 * around the label, force the label to fill the entire client area.
                 */
                Rectangle area = tip.getClientArea();
                label.setSize(area.width, area.height);
                /*
                 * Position the tooltip and ensure that it is not located off
                 * the screen.
                 */
                Point pt = new Point(item.x + item.width / 4, item.y
                        + item.height + 2);
                pt = toDisplay(pt);
                Rectangle rect = display.getBounds();
                Point tipSize = tip.getSize();
                pt.x = Math.max(0, Math.min(pt.x, rect.width - tipSize.x));
                pt.y = Math.max(0, Math.min(pt.y, rect.height - tipSize.y));
                tip.setLocation(pt);
                tip.setVisible(true);
                return;
            }
        }

        toolTipItem = null;
        if (tip != null && !tip.isDisposed() && tip.isVisible()) {
            tip.setVisible(false);
        }
    }

    private void onMouseMove(Event event) {
        if (showToolTip) {
            showToolTip(event.x, event.y);
        }

        if (!showClose) {
            return;
        }

        CTabItem item = null;
        for (int i = 0; i < items.length; i++) {
            Rectangle rect = items[i].getBounds();
            if (rect.contains(new Point(event.x, event.y))) {
                item = items[i];
                break;
            }
        }
        if (item == inactiveItem) {
            return;
        }

        inactiveCloseBar.setVisible(false);
        inactiveItem = null;

        if (item == null || item == getSelection()) {
            return;
        }

        int toolbarHeight = tabHeight - CTabItem.TOP_MARGIN
                - CTabItem.BOTTOM_MARGIN + 2; // +2 to ignore gap between focus rectangle
 Point size = inactiveCloseBar.computeSize(SWT.DEFAULT, toolbarHeight);
        int x = item.x + item.width - size.x - 2; // -2 to not overlap focus rectangle and trim
 int y = item.y + Math.max(0, (item.height - toolbarHeight) / 2);
        Rectangle toolspace = getToolSpace();
        Point folderSize = getSize();
        if ((toolspace.width == 0 || x < toolspace.x)
                && x + size.x < folderSize.x - borderRight) {
            inactiveCloseBar.setBounds(x, y, size.x, toolbarHeight);
            inactiveCloseBar.setVisible(true);
            inactiveItem = item;
        }
    }

    private void onTraverse(Event event) {
        switch (event.detail) {
        case SWT.TRAVERSE_ESCAPE:
        // TEMPORARY CODE See bug report 17372
 // case SWT.TRAVERSE_RETURN:
 case SWT.TRAVERSE_TAB_NEXT:
        case SWT.TRAVERSE_TAB_PREVIOUS:
            event.doit = true;
            break;
        case SWT.TRAVERSE_MNEMONIC:
            event.doit = onMnemonic(event);
            if (event.doit) {
                event.detail = SWT.TRAVERSE_NONE;
            }
            break;
        case SWT.TRAVERSE_PAGE_NEXT:
        case SWT.TRAVERSE_PAGE_PREVIOUS:
            event.doit = onPageTraversal(event);
            if (event.doit) {
                event.detail = SWT.TRAVERSE_NONE;
            }
            break;
        }
    }

    private boolean onPageTraversal(Event event) {
        int count = getItemCount();
        if (count == 0) {
            return false;
        }
        int index = getSelectionIndex();
        if (index == -1) {
            index = 0;
        } else {
            int offset = (event.detail == SWT.TRAVERSE_PAGE_NEXT) ? 1 : -1;
            index = (index + offset + count) % count;
        }
        setSelection(index, true);
        return true;
    }

    /**
     * Answer true if not all tabs can be visible in the receive
     * thus requiring the scroll buttons to be visible.
     */
    private boolean scroll_leftVisible() {
        return topTabIndex > 0;
    }

    /**
     * Answer true if not all tabs can be visible in the receive
     * thus requiring the scroll buttons to be visible.
     */
    private boolean scroll_rightVisible() {
        // only show Scroll buttons if there is more than one item
 // and if we are not already at the last item
 if (items.length < 2) {
            return false;
        }
        Rectangle area = getClientArea();
        int rightEdge = area.x + area.width;
        if (rightEdge <= 0) {
            return false;
        }
        if (topTabIndex > 0) {
            rightEdge -= arrowBar.getSize().x;
        }
        if (topRight != null) {
            rightEdge -= topRight.getSize().x;
        }
        CTabItem item = items[items.length - 1];
        return (item.x + item.width > rightEdge);
    }

    /**
     * Scroll the tab items to the left.
     */
    private void scroll_scrollLeft() {
        if (items.length == 0) {
            return;
        }
        setLastItem(topTabIndex - 1);
    }

    /**
     * Scroll the tab items to the right.
     */
    private void scroll_scrollRight() {
        int lastIndex = getLastItem();
        topTabIndex = lastIndex + 1;
        setItemLocation();
        correctLastItem();
        redrawTabArea(-1);
    }

    private boolean correctLastItem() {
        Rectangle area = getClientArea();
        int rightEdge = area.x + area.width;
        if (rightEdge <= 0) {
            return false;
        }
        Rectangle toolspace = getToolSpace();
        if (toolspace.width > 0) {
            rightEdge -= toolspace.width;
        }
        CTabItem item = items[items.length - 1];
        if (item.x + item.width < rightEdge) {
            setLastItem(items.length - 1);
            return true;
        }
        return false;
    }

    /**
     * Specify a fixed height for the tab items. If no height is specified,
     * the default height is the height of the text or the image, whichever
     * is greater. Specifying a height of 0 will revert to the default height.
     *
     * @param height the pixel value of the height or 0
     *
     * @exception SWTException <ul>
     * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * <li>ERROR_INVALID_ARGUMENT - if called with a height of less than 0</li>
     * </ul>
     */
    public void setTabHeight(int height) {
        checkWidget();
        if (height < 0) {
            SWT.error(SWT.ERROR_INVALID_ARGUMENT);
        }
        fixedTabHeight = true;
        if (tabHeight == height) {
            return;
        }
        tabHeight = height;
        oldSize = null;
        notifyListeners(SWT.Resize, new Event());
    }

    void resetTabSize(boolean checkHeight) {
        int oldHeight = tabHeight;
        if (!fixedTabHeight && checkHeight) {
            int tempHeight = 0;
            GC gc = new GC(this);
            for (int i = 0; i < items.length; i++) {
                tempHeight = Math.max(tempHeight, items[i].preferredHeight(gc));
            }
            gc.dispose();
            if (topRight != null) {
                tempHeight = Math.max(tempHeight, topRight.computeSize(
                        SWT.DEFAULT, SWT.DEFAULT).y);
            }
            tabHeight = tempHeight;
        }

        if (tabHeight != oldHeight) {
            oldSize = null;
            notifyListeners(SWT.Resize, new Event());
        } else {
            setItemBounds();
            redraw();
        }
    }

    /**
     *
     * @exception SWTException <ul>
     * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * <li>ERROR_INVALID_ARGUMENT - if the position value is not either SWT.TOP or SWT.BOTTOM</li>
     * </ul>
     *
     * UNDER CONSTRUCTION
     * @since 3.0
     */
    public void setTabPosition(int position) {
        checkWidget();
        if (position != SWT.TOP && position != SWT.BOTTOM) {
            SWT.error(SWT.ERROR_INVALID_ARGUMENT);
        }
        if (onBottom != (position == SWT.BOTTOM)) {
            onBottom = position == SWT.BOTTOM;
            setBorderVisible(showBorders);
            resetTabSize(true);
            // updateTabHeight(true);
 // Rectangle rectBefore = getClientArea();
 // updateItems();
 // Rectangle rectAfter = getClientArea();
 // if (!rectBefore.equals(rectAfter)) {
 // notifyListeners(SWT.Resize, new Event());
 // }
 // setItemBounds();
 // redrawTabArea(-1);
 // redraw();
 }
    }

    public int getTabPosition() {
        if (onBottom) {
            return SWT.BOTTOM;
        }
        return SWT.TOP;
    }

}

